home *** CD-ROM | disk | FTP | other *** search
/ L' Effet Pommier 3 / L'Effet Pommier - Volume 03.iso / Programmation / Alpha ƒ / Tcl / SystemCode / fortran.tcl < prev    next >
Text File  |  1996-01-25  |  21KB  |  660 lines

  1. #=============================================================================
  2. # Fortran mode definition and support procs
  3. #
  4. # Features:
  5. # 1.  Keyword colorization (slightly customizable)
  6. # 2.  Fortran-sensitive shift right/left preserve columns 1-6
  7. # 3.  Auto-indentation
  8. # 4.  Line-breaking with Ctl-Opt-J (a la emacs)
  9. # 5.  Subroutine indexing
  10. # 6.  Cmd-double-click subroutine and include-file lookup
  11. # 7.  Customizable comment and continuation characters
  12. #
  13. #------------------------------------------------------------------------------
  14. # Author: Tom Pollard <pollard@chem.columbia.edu>
  15. #
  16. # To Do:  work around grep failure for Unix-format tag files
  17. #
  18. #  1/96 - FortMarkFile no longer marks F90 "end subroutine ..." statements
  19. #         more F90 keywords (will they never cease?)
  20. #  1/96 - user-selectable comment and continuation characters
  21. #         complete F90 keyword set (Thomas Bewley <bewley@rayleigh.stanford.edu>) 
  22. #         F90 functions and comparison operators optionally colorized ( " " )
  23. #         more complete set of C preprocessor commands colorized
  24. #         fixed case-sensitivity problem in line-indent routines
  25. #  1/96 - minor FortDblClick bug fix
  26. # 12/95 - more complete keyword set for F90 and HPF (from Tom Scavo)
  27. # 12/95 - cpp keyword colorization (George Nurser <g.nurser@soc.soton.ac.uk>)
  28. #         cmd-dbl-click supports cpp #include now
  29. # 11/95 - added FortBreakLine
  30. #         fixed case-sensitivity bug
  31. # 10/95 - fixed Cmd-Dbl-Click handler to deal w/ new(?) tag file format and
  32. #            improve performance (fortFindSub)
  33. #  9/95 - fixed getFortPrev bug with numbered lines
  34. #       - shiftLeft/Right revert to normal behavior on ill-formatted lines
  35. #  8/95 - auto-indentation is finally speedy and robust
  36. #  5/95 - added Cmd-Dbl-Click handler
  37. #       - added auto-indentation
  38. # 12/94 - fixed funcExpr, FortMarkFile search expressions
  39. #       - changed comment character from 'C' to 'c' (should be case-insensitive!)
  40. #       - added 'include' keyword
  41. #       - added FortShiftRight and FortShiftLeft procs
  42. #------------------------------------------------------------------------------
  43. proc dummyFort {} {}
  44.  
  45. newModeVar Fort sortedIsDefault    {0} 1
  46. newModeVar Fort wordWrap        {0}    1
  47. newModeVar Fort funcExpr    {^[^cC*!][ \t]*(subroutine|[ \ta-z*0-9]*function|entry).*$} 0
  48. newModeVar Fort autoMark        {0}    1
  49. newModeVar Fort electricTab        {1}    1
  50.  
  51. # newModeVar Fort    prefixString    {c}    0
  52. newModeVar Fort    continueChar    {$}    0
  53. newModeVar Fort    commentChar    {c}    0
  54. newModeVar Fort    colorFuncs    {0}    1
  55. newModeVar Fort    colorOpers    {0}    1
  56.  
  57. newModeVar Fort indentComment    {0}    1
  58. newModeVar Fort markTag            {{}} 0
  59.  
  60. #=============================================================================
  61. # Colorize Fortran keywords
  62. #
  63. proc fortColorKeywords {{color blue} {comment red} {specialChars black}} {
  64.     global FortmodeVars
  65.  
  66.     set FortKeywords { 
  67.         allocatable allocate assign backspace block call character close common 
  68.         complex contains continue cycle data deallocate dimension do double else 
  69.         elseif end enddo endfile endif entry equivalence exit external extrinsic 
  70.         forall format function goto if implicit include inquire integer intent 
  71.         interface intrinsic logical module namelist nullify open optional 
  72.         parameter pause pointer precision print private program public pure read 
  73.         real recursive return rewind save sequence stop subroutine target then 
  74.         use where while write assignment case default elsewhere endfile go none 
  75.         operator procedure select to type
  76.     }
  77.     
  78.     if {$specialChars == "black"} {
  79.         regModeKeywords -e $FortmodeVars(commentChar) -c $comment -k $color Fort $FortKeywords  -i {=}  -i {*}  -i {/}  -i {+}  -i {-}  -i {,}  -i {(} -i {)} -I $specialChars
  80.     } else {
  81.         regModeKeywords -e $FortmodeVars(commentChar) -c $comment -k $color Fort $FortKeywords  
  82.     }
  83.     unset FortKeywords
  84.  
  85. #=============================================================================
  86. # Colorize selected C preprocessor keywords
  87. #
  88. proc fortColorCPP {{color green}} {
  89.     set CPPKeywords  {
  90.         #if #endif #include #else #define #undef #ifdef #ifndef
  91.     }
  92.     regModeKeywords -a  -k $color Fort $CPPKeywords
  93.     unset CPPKeywords
  94. }
  95.  
  96.  
  97. #=========================================================================
  98. # Colorize Fortran operators
  99. #
  100. proc fortColorOpers {{color green}} {
  101.     set FortOperators {
  102.         eq ne lt le gt ge not and or eqv neqv true false
  103.     }
  104.     regModeKeywords -a -k $color Fort $FortOperators
  105.     unset FortOperators
  106. }
  107.  
  108. #=========================================================================
  109. # Colorize Fortran function keywords
  110. #
  111. proc fortColorFuncs {{color green}} {
  112.     # Fortran bit functions
  113.     #
  114.     set BitKeywords {
  115.         bit_size btest iand ibclr ibits ibset ieor ior ishft ishftc mvbits not
  116.     }
  117.     regModeKeywords -a -k $color Fort $BitKeywords
  118.     unset BitKeywords
  119.     
  120.     # Fortran intrinsic functions
  121.     #
  122.     set IntrinsicKeywords {
  123.         abs acos aimag asin atan atan2 conjg cos cosh dble dim dprod exp ichar 
  124.         len lge lgt lle llt log log10 max min mod sign sin sinh sqrt tan tanh 
  125.         iabs dabs cabs dacos dint dnint dasin datan datan2 dcos ccos dcosh idim 
  126.         ddim dexp cexp ifix idint alog ddlog clog alog10 dlog10 max0 amax0 max1 
  127.         amax1 dmax1 min0 amin0 min1 amin1 dmin1 amod dmod idnint float sngl 
  128.         isign dsign dsin csin dsinh dsqrt csqrt dtan dtanh aint anint char cmplx 
  129.         index int nint achar adjustl adjustr all allocated any associated 
  130.         bit_size btest ceiling count cshift date_and_time digits dot_product 
  131.         eoshift epsilon exponent floor fraction huge iachar iand ibclr ibits 
  132.         ibset ieor ior ishft ishftc kind lbound len_trim logical matmul 
  133.         maxexponent maxloc maxval merge minexponent minloc minval modulo mvbits 
  134.         nearest not pack precision present product radix random_number 
  135.         random_seed range repeat reshape rrspacing scale scan selected_int_kind 
  136.         selected_real_kind set_exponent shape size spacing spread sum 
  137.         system_clock tiny transfer transpose trim ubound unpack verify
  138.     }
  139.     regModeKeywords -a -k $color Fort $IntrinsicKeywords
  140.     unset IntrinsicKeywords    
  141. }
  142.  
  143. fortColorKeywords blue red black
  144. fortColorCPP green
  145.  
  146. #=============================================================================
  147. # Special Fortran keybindings
  148. #
  149. bind '\[' <c>  FortShiftLeft Fort
  150. bind '\[' <co> FortShiftLeftSpace Fort
  151. bind '\]' <c>  FortShiftRight Fort
  152. bind '\]' <co> FortShiftRightSpace Fort
  153.  
  154. bind '\t'       doATab Fort
  155. bind '\t' <o>     {doATab 1} Fort
  156. bind '\t' <z>     {doATab 1} Fort
  157.  
  158. bind 'j'  <zo> FortBreakLine Fort
  159.  
  160. trace variable FortmodeVars(commentChar) w shadowFort
  161. trace variable FortmodeVars(colorFuncs) w shadowFort
  162. trace variable FortmodeVars(colorOpers) w shadowFort
  163.  
  164. #=============================================================================
  165. # Update colorization when Fortran mode variables are changed
  166. #
  167. proc shadowFort {name1 name2 op} {
  168.     global HOME FortmodeVars
  169.     if {$name1 == "FortmodeVars" && $op == "w"} {
  170.         switch $name2 {
  171.             "colorFuncs"    {
  172.                 if {$FortmodeVars(colorFuncs)} {
  173.                     fortColorFuncs green
  174.                 } else {
  175.                     fortColorFuncs black
  176.                 }
  177.              }
  178.             "colorOpers"    {
  179.                 if {$FortmodeVars(colorOpers)} {
  180.                     fortColorOpers green
  181.                 } else {
  182.                     fortColorOpers black
  183.                 }
  184.              }
  185.             "commentChar" {    
  186.                 fortColorKeywords blue red black
  187.             }
  188.             default {
  189.                 return
  190.             }
  191.         }
  192.     }
  193. }
  194.  
  195. #=============================================================================
  196. #
  197. proc FortMarkFile {} {
  198.     global FortmodeVars
  199.     set tag [quoteExpr2 $FortmodeVars(markTag)]
  200.     
  201.     set pat0 {^.*(subroutine|.*function|entry|program).*$}
  202.     set pat1 {^[^cC*!]([ \ta-z*0-9]*)(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  203.     set end [maxPos]
  204.     set pos 0
  205.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $pat1 $pos} mtch]} {
  206.         regexp -nocase $pat1 [eval getText $mtch] allofit valtyp subtyp name
  207.         set start [lineStart [lindex $mtch 0]]
  208.         set next [nextLineStart $start]
  209.         set pos $next
  210.         if {! [regexp -nocase "end" $valtyp mtch]} {
  211.             set inds([lineStart $start]) $name
  212.         }
  213.         
  214.     }
  215.     
  216.     set pat2 "^(c+${tag})\[ \t\]*(\[^\n\r\]*\[^ \t\])\[^ \t\]*\$"
  217.     set pos 0
  218.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $pat2 $pos} mtch]} {
  219.         regexp -nocase $pat2 [eval getText $mtch] allofit cc comment
  220.         regsub -all {[\/\(\)]} $comment {} comment
  221.         set start [lindex $mtch 0]
  222.         set end [nextLineStart $start]
  223.         set pos $end
  224.         set inds([lineStart $start]) $comment
  225.     }
  226.     
  227.     if {[info exists inds]} {
  228.         foreach f [lsort -integer [array names inds]] {
  229.             set next [nextLineStart $f ]
  230.             setNamedMark $inds($f) $f $f $f
  231.         }
  232.     }
  233. }
  234.  
  235. #================================================================================
  236. # Block shift left and right for Fortran mode (preserves cols 1-6)
  237. #================================================================================
  238.  
  239. proc FortShiftLeft {} {
  240.     global shiftChar
  241.     doFortShiftLeft "\t"
  242.     
  243. }
  244. proc FortShiftLeftSpace {} {
  245.     global shiftChar
  246.     doFortShiftLeft " "
  247. }
  248.  
  249. proc doFortShiftLeft {shiftChar} {
  250.     set start [lineStart [getPos]]
  251.     set end [nextLineStart [expr [selEnd] - 1]]
  252.     if {$start >= $end} {set end [nextLineStart $start]}
  253.     
  254.     set text [split [getText $start [expr $end - 1]] "\r"]
  255.     
  256.     set textout ""
  257.     
  258.     set pat {^([cC]|[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].| *[0-9]*\t)(.*)$}
  259.     foreach line $text {
  260.         if {[regexp $pat $line mtch pref body]} {
  261.             if {[string index $body 0] == $shiftChar} {
  262.                 lappend textout $pref[string range $body 1 end]
  263.             } else {
  264.                 lappend textout $line
  265.             }
  266.  
  267.         } elseif {[string index $line 0] == $shiftChar} {
  268.             lappend textout [string range $line 1 end]
  269.  
  270.         } else {
  271.             lappend textout $line
  272.         }
  273.     }
  274.  
  275.     set text [join $textout "\r"]    
  276.     replaceText $start [expr $end - 1] $text
  277.     select $start [expr 1 + $start + [string length $text]]
  278. }
  279.  
  280. proc FortShiftRight {} {
  281.     global shiftChar
  282.     doFortShiftRight "\t"
  283.     
  284. }
  285. proc FortShiftRightSpace {} {
  286.     global shiftChar
  287.     doFortShiftRight " "
  288. }
  289.  
  290. proc doFortShiftRight {shiftChar} {
  291.     set start [lineStart [getPos]]
  292.     set end [nextLineStart [expr [selEnd] - 1]]
  293.     if {$start >= $end} {set end [nextLineStart $start]}
  294.     
  295.     set text [split [getText $start [expr $end - 1]] "\r"]
  296.     
  297.     set textout ""
  298.     
  299.     set pat {^([cC]|[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].| *[0-9]*\t)(.*)$}
  300.     foreach line $text {
  301.         if {[regexp $pat $line mtch pref body]} {
  302.             lappend textout $pref$shiftChar$body
  303.         } else {
  304.             lappend textout $shiftChar$line
  305.         }
  306.     }
  307.     
  308.     set text [join $textout "\r"]    
  309.     replaceText $start [expr $end - 1] $text
  310.     select $start [expr 1 + $start + [string length $text]]
  311. }
  312.  
  313. proc FortBreakLine {} {
  314.     global FortmodeVars
  315.     set pos [getPos]
  316.     set line [getText [lineStart $pos] [expr [nextLineStart $pos]-1]]
  317.     if {[regexp {^[cC*!]} $line char]} {
  318.         insertText "\n$char "
  319.     } else {
  320.         set char $FortmodeVars(continueChar)
  321.         insertText "\n     $char"
  322.     }
  323.     FortindentLine
  324. }
  325.  
  326. #=============================================================================
  327. # Cmd-double-clicking opens include files, jumps to subroutine definitions,
  328. # and follows tags.
  329. #
  330. proc FortDblClick {from to} {
  331.     global tagFile
  332.     set pat1 {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry)[ \t]+}
  333.     set incPat {^[^cC*!][ \t]*include[ \t]*['"]([^'"]+)['"]}
  334.  
  335.     # First check whether an 'include' was clicked
  336.     set line [getText [lineStart $from] [expr [nextLineStart $to] - 1]]
  337.     if {[regexp -nocase $incPat $line allofit fname]} {
  338.         set path [absolutePath $fname]
  339.         if {[catch {openFileQuietly $path}]} { 
  340.             message "include file \'$fname\' not found in source folder"
  341.         }
  342.         return
  343.     }
  344.     
  345.     select $from $to
  346.     set text [getSelect]
  347.     
  348.     # First check current file for subroutine definition,...
  349.     if {![catch {fortFindSub $text} mtch]} { 
  350.         regexp -nocase $pat1 [eval getText $mtch] allofit subtyp name
  351.         pushMark
  352.         display [lindex $mtch 0]
  353. #         eval select $mtch
  354.         message "press <Ctl .> to return to original cursor position"
  355.  
  356.     # ...then check tags file.
  357.     } else {
  358.         message "Searching tags file..."
  359.         set lines [grep "^$text'" $tagFile]
  360.         if {[regexp {'(.*)'} $lines dummy fname]} { 
  361.             pushMark
  362.             if {[string match "*$fname*" [winNames -f]]} {
  363.                 bringToFront $fname
  364.             } else {
  365.                 edit $fname
  366.             }
  367.             set inds [fortFindSub $text]
  368. #             set inds [search -s -f 1 -r 1 -i 1 "$pat1$text" 0]
  369.             display [lindex $inds 0]
  370. #             eval select $inds
  371.             message "press <Ctl .> to return to original cursor position"
  372.         }
  373.     }
  374. }
  375.  
  376. # Speedy search for a Fortran subroutine.  Performance is dramatically 
  377. # improved by scanning for the name alone first, rather than running 
  378. # complicated regexp search on the entire file.
  379. #
  380. proc fortFindSub {name} {
  381.     set pat1 {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry)[ \t]+}
  382.     set pos 0
  383.     while {![catch {search -s -f 1 -r 0 -m 0 -i 1 $name $pos} mtch]} {
  384.         set beg [lineStart [lindex $mtch 0]]
  385.         set end [expr [nextLineStart [lindex $mtch 1]] -1]
  386.         set line [getText $beg $end]
  387.         if {[regexp  -nocase $pat1$name $line allofit subtyp name]} {
  388.             return $mtch 
  389.         } else {
  390.             set pos [lindex $mtch 1]
  391.         }
  392.     }
  393.     error "Subroutine \"$name\" not found"
  394. }
  395.  
  396. #=============================================================================
  397. # Fortan auto-indentation
  398. #
  399. # Logic:
  400. #    0.    Identify previous line
  401. #            a) ignore comments and continuation lines
  402. #            b) if current line is a CONTINUE that matches a DO, use the
  403. #                first corresponding DO as the previous line
  404. #
  405. #    1.    Find leading whitespace for previous line
  406. #
  407. #    2.    Increase whitespace if previous line starts a block, i.e.,
  408. #            a) DO loop
  409. #            b) IF ... THEN 
  410. #            c) ELSE
  411. #
  412. #    3.    Decrease whitespace if current line ends a block, i.e.,
  413. #            a) ELSE || ENDIF || END IF || ENDDO || END DO
  414. #            b) <linenum> CONTINUE matching a preceding DO
  415. #
  416. #        or if previous line ends a DO loop on an executable statement, i.e.,
  417. #            c) <linenum> (not CONTINUE) matching a preceding DO
  418. #
  419. ####################################################################################
  420. # Fortan auto-indentation
  421. #
  422. proc FortindentLine {} {    
  423.     set bol [lineStart [getPos]]
  424.     set eol [expr [nextLineStart $bol] - 1]
  425.     Fortindent $bol $eol
  426. }
  427.  
  428. proc FortindentRegion {} {    
  429.     Fortindent [getPos] [selEnd]
  430. }
  431.  
  432. ####################################################################################
  433. # Fortan auto-indentation of a specified region
  434. #
  435. proc Fortindent {pos0 pos1} {
  436.     global fortDooz fortPrevLine fortTop msg
  437.     global FortmodeVars
  438.  
  439.     set tag [quoteExpr2 $FortmodeVars(markTag)]
  440.     set doComment $FortmodeVars(indentComment)
  441.  
  442.     # Define regexps
  443.     set subPat {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  444.     set bolPat {^[^cC*!\n\r][ \t]*[^ \t\n\r][^\r\n]*$}
  445.     set mtPat {^[ \t]*$}
  446.     set tab "    "
  447.     
  448.     set contPat {^     ([^ \t\n\r])[^\r\n]*$}
  449.     set lnumPat {^([ \t]*)([0-9]*)([ \t]*)(.*)$}
  450.     set comPat "^(\[cC*!\]+(${tag})?)(\[ \t\]*)(.*)\$"
  451.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  452.     set tailPat {[^\r\n]*$}
  453.     
  454.     set bobPat {^(if[^\n\r]*then|else|do)}
  455.     set eobPat {^(end[ \t]*if|end[ \t]*do|else)}
  456.     set enddoPat {^(end[ \t]*do|continue)}
  457.     
  458. #     set fortTop [fortSubTop $pos0]
  459.     set fortTop -1
  460.     
  461.     catch {unset fortDooz}
  462.     set fortPrevLine ""
  463.     
  464.     # Loop over region line by line
  465.     set from [lindex [posToRowCol $pos0] 0]
  466.     set to [lindex [posToRowCol $pos1] 0]
  467.     
  468.     while {$from <= $to} {        
  469.         set msg "Indenting line $from"
  470.         message $msg
  471.         set bol [lineStart [rowColToPos $from 0]]
  472.         set eol [expr [nextLineStart $bol] - 1]
  473.         set thisLine [getText $bol $eol]
  474.         goto $bol
  475.         
  476.         # Check whether we're entering a new routine
  477.         #
  478.         if {[regexp $subPat $thisLine allofit subType subName]} {
  479.             # alertnote "entering subr: \/$subName\/"
  480.             set fortTop $bol
  481.             catch {unset fortDooz}
  482.         } 
  483.         
  484.         # Is the current line a comment line...
  485.         #        
  486.         if {[regexp $comPat $thisLine allofit cc tag pre body]} {
  487.             if {$FortmodeVars(indentComment) > 0} {
  488.                 set body [string trimright $body]
  489.                 # alertnote "comment line: \/$pre\/$body\/"
  490.                 set lwhite "$cc     "
  491.                 
  492.                 replaceText $bol $eol $lwhite$body
  493.             }
  494.             
  495.         # ... or a line of code (possibly empty)?
  496.         #    
  497.         } elseif {[regexp $lnumPat $thisLine allofit pre lnum post body]} {
  498.             set body [string trimright $body]
  499.             # alertnote "line: \/$pre\/$lnum\/$post\/$body\/"
  500.             
  501.             # is it a continuation line?
  502.             #
  503.             if {(![regexp {\t} $pre]) && ([string length $pre] == 5)} {
  504.                 set cont [string index $lnum$post$body 0]
  505.                 set body [string trimleft [string range $lnum$post$body 1 end]]
  506.             } else {
  507.                 set cont {}
  508.             }
  509.             # alertnote "cont: \/$cont\/"
  510.             
  511.             # get whitespace for preceding line
  512.             set enddo [getFortPrev $bol $lnum]
  513.             set lwhite [getFortLwhite $bol]
  514.             
  515.             # if this line ends a block, decrease the whitespace
  516.             if {[regexp $eobPat $body] || ($enddo && [regexp -nocase $enddoPat $body])} {
  517.                 set lwlen [expr [string length $lwhite] - 4]
  518.                 set lwhite [string range $lwhite 0 $lwlen]
  519.             } 
  520.             
  521.             if {[string length $lnum]} {
  522.                 if {[string index $lwhite 0] != $tab} {
  523.                     set lwhite [string range $lwhite [expr [string length $lnum] +1] end]
  524.                 }
  525.                 set lnum " $lnum"
  526.             }
  527.             # alertnote "lwhite: \/$lwhite\/ len: [string length $lwhite]"
  528.             # message "$msg : replacing text      "
  529.             
  530.             if {[string length $cont]} {
  531.                 replaceText $bol $eol "     $cont$lwhite$body"    
  532.             } else {
  533.                 replaceText $bol $eol $lnum$lwhite$body
  534.                 if {[string length $body] > 0} {
  535.                     set fortPrevLine $lnum$lwhite$body
  536.                 }
  537.             }
  538.         } else {
  539.             # message "$msg : Couldn't parse line         "
  540.         }
  541.         
  542.         # message "$msg : Done                "
  543.         incr from
  544.     }
  545. }
  546.  
  547. proc getFortLwhite {bol} {
  548.     global fortDooz fortPrevLine fortTop msg
  549.     # Define regexps
  550.     set tab "    "
  551.     set lnumPat {^([ \t]*)([0-9]*)([ \t]*)(.*)$}
  552.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  553.     set bobPat {^(if[^\n\r]*then|else|do)}
  554.     set enddoPat {^(end[ \t]*do|continue)}
  555.     
  556.     if {[regexp $lnumPat $fortPrevLine allofit pre0 lnum0 post0 body0]} {
  557.         # alertnote "prevLine: \/$pre0\/$lnum0\/$post0\/$body0\/"
  558.         
  559.         if {[string length $lnum0]} {
  560.             if {[string index $post0 0] == $tab} {
  561.                 set lwhite $post0
  562.             } else {
  563.                 regsub -all {[0-9]} $pre0$lnum0$post0 { } lwhite
  564.             }
  565.         } else {
  566.             set lwhite $pre0
  567.         }
  568.         # alertnote "lwhite: \/$lwhite\/ len: [string length $lwhite]"
  569.         # message "$msg : got lwhite (initial)"
  570.         
  571.         # if there's a line number and it's not a CONTINUE or ENDDO, 
  572.         # then check for a matching DO statement and adjust 
  573.         # indentation if found
  574.         #
  575.         if {[string length $lnum0] && ![regexp -nocase $enddoPat $body0]} {
  576.             if {[getFortPrev [lineStart [expr $bol - 1]] $lnum0]} {
  577.                 set lwlen [expr [string length $lwhite] - 4]
  578.                 set lwhite [string range $lwhite 0 $lwlen]
  579.  
  580.             }
  581.         }
  582.         
  583.         # If the preceeding line begins a block (IF-THEN, DO, or ELSE),
  584.         # then increase the whitespace
  585.         #    
  586.         if {[regexp -nocase $bobPat $body0]} {
  587.             set lwhite "$lwhite   "
  588.             
  589.             if {[regexp -nocase "$doPat\(\[0-9\]+\)" $body0 mtch donum]} {
  590.                 set eol [expr [nextLineStart $bol] - 1]
  591.                 set fortDooz($donum) [getText $bol $eol]
  592.             }
  593.         }
  594.         # message "$msg : got lwhite (final)  "
  595.     }
  596.     return "$lwhite"
  597. }
  598.  
  599. proc getFortPrev {bol lnum} {        
  600.     global fortDooz fortPrevLine fortTop msg
  601.     # Define regexps
  602.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  603.     set bolPat {^[^cC*!\n\r][ \t]*[^ \t\n\r][^\r\n]*$}
  604.     set contPat {^     ([^ \t\n\r])[^\r\n]*$}
  605.  
  606.     # if there's a line number, check for a matching DO statement ...
  607.     if {[string length $lnum]} {
  608.         if {[lsearch [array names fortDooz] $lnum] >= 0} {
  609.             set fortPrevLine $fortDooz($lnum)
  610.             return 1
  611.         } else {
  612.             if {$fortTop < 0} {
  613.                 set fortTop [fortSubTop $bol]
  614.             }
  615.             if {![catch {search -s -f 0 -r 1 -i 1 -l $fortTop $doPat$lnum [expr $bol -1]} dolst]} {
  616.                 set fortPrevLine [eval getText $dolst]
  617.                 set fortDooz($lnum) $fortPrevLine
  618.                 # alertnote "doLine0: \/$fortPrevLine\/"
  619.                 return 1
  620.             }
  621.         }
  622.     }
  623.         
  624.     # ... otherwise find the first preceding non-comment, non-continuation line
  625.     if {[string length $fortPrevLine] == 0} {
  626.         if {[catch {
  627.             set lst [search -s -f 0 -r 1 -i 1 -s $bolPat [expr $bol-1]]
  628.             set fortPrevLine [eval getText $lst]
  629.             while {[regexp -nocase $contPat $fortPrevLine]} {
  630.                 set lst [search -s -f 0 -r 1 -i 1 $bolPat [expr [lindex $lst 0] - 1]]
  631.                 set fortPrevLine [eval getText $lst]
  632.             }
  633.         }]} {
  634.             # if search fails, we're at the top of a file, so reset indentation
  635.             set fortPrevLine "      continue"
  636.         }
  637.     }
  638.     
  639.     # alertnote "prevLine: \/$fortPrevLine\/"
  640.     # message "$msg : got prevLine"
  641.     return 0
  642. }
  643.  
  644. # Find the beginning of the current subroutine
  645. #
  646. proc fortSubTop {{pos 0}} {
  647.     if {$pos == 0} {
  648.         set pos [lineStart [getPos]]
  649.     }
  650.     set subPat {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  651.     
  652.     if {![catch {search -s -f 0 -r 1 -m 0 -i 1 $subPat $pos} sublst]} {
  653.         # set subLine [eval getText $sublst]
  654.         # alertnote "subLine: \/$subLine\/"
  655.         return [lindex $sublst 0]
  656.     } else {
  657.         return 0
  658.     } 
  659. }